/* Definitions for event handling.

   This file is part of khipu.

   khipu is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */

#include "event.hpp"
#include "thread.hpp"
#include "function.hpp"
#include "memory.hpp"
#include "symbol.hpp"
#include "str.hpp"

KP_DECLS_BEGIN

static object ev_handlers[NPENDEV + 1];

#ifdef KP_PLATFORM_UNIX

static result<object>
sigint_handler (interpreter *interp, object *, int)
{
  return (interp->raise ("intr-error", "thread was interrupted"));
}

static void
sighandler (int sig)
{
  interpreter::self()->set_ev (sig);
}

#endif

object add_evhandler (interpreter *interp, unsigned int ev, object fct)
{
  if (--ev >= NPENDEV)
    kp_return (UNBOUND);

  object *ptr = &ev_handlers[ev];
  if (singlethr_p ())
    {
      interp->retval = *ptr;
      *ptr = fct;
    }
  else
    interp->retval = atomic_swap ((atomic_t *)ptr, fct);

  return (interp->retval);
}

object get_evhandler (interpreter *interp, unsigned int ev)
{
  if (--ev >= NPENDEV)
    kp_return (UNBOUND);

  kp_return (ev_handlers[ev]);
}

struct builtin_event
{
  native_function fct;
  string name;
  symbol sym;

  void init (interpreter *interp, const void *data,
             native_function::fn_type fn, int idx)
    {
      this->name.local_init (data);
      this->sym.vo_type = typecode::SYMBOL;
      this->sym.value = UNBOUND;
      this->sym.pkg = NIL;
      this->sym.name = ensure_mask(&this->name)->as_obj ();
      this->fct.vo_full = function_base::native_flag;
      this->fct.vo_type = typecode::FCT;
      this->fct.min_argc = this->fct.max_argc = 1;
      this->fct.name = ensure_mask(&this->sym)->as_obj ();
      this->fct.fct = fn;

      ev_handlers[idx - 1] = ensure_mask(&this->fct)->as_obj ();
    }
};

static int
do_init_events (interpreter *interp)
{
  int ret = init_op::call_deps (interp, &init_symbols);
  if (ret != init_op::result_ok)
    return (ret);

  for (unsigned int i = 0; i < NPENDEV; ++i)
    ev_handlers[i] = UNBOUND;

#ifdef KP_PLATFORM_UNIX

  static builtin_event sigint_event;
  sigint_event.init (interp, "sigint-handler", sigint_handler, SIGINT);

  struct sigaction sa;
  sigfillset (&sa.sa_mask);
  sa.sa_flags = 0;
  sa.sa_handler = sighandler;
  sigaction (SIGINT, &sa, 0);

#endif

  static builtin_event gcreq_event;
  gcreq_event.init (interp, "gc-request", gcreq_handler, GCREQ_EV);

  return (init_op::result_ok);
}

init_op init_event (do_init_events, "events");

KP_DECLS_END
